#include "EntityGrid.h"
#include "Base/Exceptions.h"
#include "Base/Logger.h"

EntityBox::EntityBox()
{
	mpt_deques.resize(1);
}
EntityBox::EntityBox(int size)
{
	mpt_deques.resize(size);
}
std::deque<int> EntityBox::getEntityDeque(int type) const
{
    Assert(type >= 0 && type < mpt_deques.size());
	return	mpt_deques[type];
}
void EntityBox::addEntity(int type, int id)
{
    Assert(type >= 0 && type < mpt_deques.size());
	mpt_deques[type].push_back(id);
}
void EntityBox::deleteEntity(int type, int id)
{
    Assert(type >= 0 && type < mpt_deques.size());
	std::deque<int>::iterator itr;
    for(itr = mpt_deques[type].begin(); itr != mpt_deques[type].end();)
    {
        if(*itr == id)
        {
            mpt_deques[type].erase(itr);
            itr = mpt_deques[type].end();
        }
        else
            itr++;
    }
}
void EntityBox::clearEntities(int type)
{
    Assert(type >= 0 && type < mpt_deques.size());
	mpt_deques[type].clear();
}
void EntityBox::clearAllEntities()
{
    for(std::vector<std::deque<int> >::iterator itr = mpt_deques.begin(); itr!=mpt_deques.end(); itr++)
	{
		(*itr).clear();
	}
}

EntityGrid::EntityGrid(int X, int Y, int sizeBox):mpt_size(X,Y)
{
    mpt_boxes.resize(X*Y, EntityBox(sizeBox));
}
EntityGrid::EntityGrid(const sf::Vector2i& size, int sizeBox):mpt_size(size)
{
    mpt_boxes.resize(size.x*size.y, EntityBox(sizeBox));
}

void EntityGrid::addEntity(int type, int id, const sf::Vector2f& position)
{
    int x = position.x;
    int y = position.y;
	if(x < 0 || y < 0)
		return;
    x/=BOXSIZE;
    y/=BOXSIZE;

    mpt_boxes[x*mpt_size.y+y].addEntity(type,id);
	mpt_entitiesToBoxes.insert(std::pair<int,int>(id, x*mpt_size.y+y));
}
void EntityGrid::deleteEntity(int type, int id)
{
	std::map<int, int>::iterator itr = mpt_entitiesToBoxes.find(id);
	if(itr != mpt_entitiesToBoxes.end())
	{
		mpt_boxes[itr->second].deleteEntity(type,id);

		mpt_entitiesToBoxes.erase(itr);
	}
}
std::deque<int> EntityGrid::getNeighbourhoodAt(int type, const sf::Vector2f& position) const
{
    int x = position.x;
    int y = position.y;
	
    x/=BOXSIZE;
    y/=BOXSIZE;

    std::deque<int> entities;
    std::deque<int> temp;
	
	if(x >= 0 && y >= 0 && x*mpt_size.y+y < mpt_size.x*mpt_size.y)
	{
		temp= mpt_boxes[x*mpt_size.y+y].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x >= 0 && y - 1 >= 0 && x*mpt_size.y+y-1 < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[x*mpt_size.y+y-1].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x >= 0 && y + 1 >= 0 && x*mpt_size.y+y+1 < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[x*mpt_size.x+y+1].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x + 1 >= 0 && y >= 0 && (x+1)*mpt_size.y+y < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[(x+1)*mpt_size.y+y].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x + 1 >= 0 && y-1 >= 0 && (x+1)*mpt_size.y+y-1 < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[(x+1)*mpt_size.y+y-1].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x + 1 >= 0 && y+1 >= 0 && (x+1)*mpt_size.y+y+1 < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[(x+1)*mpt_size.y+y+1].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x - 1 >= 0 && y >= 0 && (x-1)*mpt_size.y+y < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[(x-1)*mpt_size.y+y].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x - 1 >= 0 && y-1 >= 0 && (x-1)*mpt_size.y+y-1 < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[(x-1)*mpt_size.y+y-1].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
	if(x - 1 >= 0 && y+1 >= 0 && (x-1)*mpt_size.y+y+1 < mpt_size.x*mpt_size.y)
	{
		temp = mpt_boxes[(x-1)*mpt_size.y+y+1].getEntityDeque(type);
		entities.insert(entities.end(), temp.begin(), temp.end());
	}
    return entities;
}
void EntityGrid::deleteNeighbourhood(int type, const sf::Vector2f& position)
{
    int x = position.x;
    int y = position.y;
    x/=BOXSIZE;
    y/=BOXSIZE;

	if(x>= 0 && y >= 0 && x*mpt_size.y+y < mpt_size.x*mpt_size.y)
		deleteBox(x*mpt_size.y+y, type);
	if(x>= 0 && y-1 >= 0 && x*mpt_size.y+y-1 < mpt_size.x*mpt_size.y)
		deleteBox(x*mpt_size.y+y-1, type);
	if(x>= 0 && y+1 >= 0 && x*mpt_size.y+y+1 < mpt_size.x*mpt_size.y)
		deleteBox(x*mpt_size.y+y+1, type);

	if(x+1>= 0 && y >= 0 && (x+1)*mpt_size.y+y < mpt_size.x*mpt_size.y)
		deleteBox((x+1)*mpt_size.y+y, type);
	if(x+1>= 0 && y-1 >= 0 && (x+1)*mpt_size.y+y-1 < mpt_size.x*mpt_size.y)
		deleteBox((x+1)*mpt_size.y+y-1, type);
	if(x+1>= 0 && y+1 >= 0 && (x+1)*mpt_size.y+y+1 < mpt_size.x*mpt_size.y)
		deleteBox((x+1)*mpt_size.y+y+1, type);

	if(x-1>= 0 && y >= 0 && (x-1)*mpt_size.y+y < mpt_size.x*mpt_size.y)
		deleteBox((x-1)*mpt_size.y+y, type);
	if(x-1>= 0 && y-1 >= 0 && (x-1)*mpt_size.y+y-1 < mpt_size.x*mpt_size.y)
		deleteBox((x-1)*mpt_size.y+y-1, type);
	if(x-1>= 0 && y+1 >= 0 && (x-1)*mpt_size.y+y+1 < mpt_size.x*mpt_size.y)
		deleteBox((x-1)*mpt_size.y+y+1, type);
}
void EntityGrid::deleteBox(int box, int type)
{
	std::deque<int> deque = mpt_boxes[box].getEntityDeque(type);
	for(std::deque<int>::iterator itr = deque.begin(); itr != deque.end(); itr++)
	{
		deleteEntity(type, *itr);
	}
}
void EntityGrid::resize(const sf::Vector2i& size)
{
    mpt_boxes.clear();
    mpt_boxes.resize(size.x*size.y);
	mpt_entitiesToBoxes.clear();
}

void EntityMap::deleteEntity(const sf::Vector2i& position)
{
	if(position.x>=0 && position.y>=0 && position.x<mpt_size.x && position.y<mpt_size.y)
	{
		deleteEntity(mpt_entities[position.x * mpt_size.y + position.y]);
	}
}

EntityMap::EntityMap(int X, int Y):mpt_size(X,Y)
{
    mpt_entities.resize(X*Y);
}
EntityMap::EntityMap(const sf::Vector2i& size):mpt_size(size)
{
    mpt_entities.resize(size.x*size.y);
}
void EntityMap::addEntity(int id, const sf::Vector2i& position)
{
    if(position.x>=0 && position.y>=0 && position.x<mpt_size.x && position.y<mpt_size.y)
	{
		mpt_entities[position.x*mpt_size.y + position.y] = id;
		mpt_entityIDtoPos.insert(std::pair<int, sf::Vector2i>(id, position));
	}
}
int EntityMap::getEntity(const sf::Vector2i& position) const
{
    if(position.x>=0 && position.y>=0 && position.x<mpt_size.x && position.y<mpt_size.y)
        return mpt_entities[position.x*mpt_size.y + position.y];
    else
        -1;
}
void EntityMap::clearEntities()
{
    mpt_entities.clear();
	mpt_entityIDtoPos.clear();
}
void EntityMap::resize(const sf::Vector2i& size)
{
    clearEntities();
    mpt_entities.resize(size.x*size.y);
	mpt_size = size;
	mpt_entityIDtoPos.clear();
}
std::vector<int> EntityMap::getNeighbourhoodAt(const sf::Vector2i& position, int rayX, int rayY)
{
	if(position.x>=0 && position.y>=0 && position.x<mpt_size.x && position.y<mpt_size.y)
	{
		std::vector<int> result;
		for(int i = position.x - rayX; i < position.x + rayX; i++)
		for(int j = position.y - rayY; j < position.y + rayY; j++)
		{
			if(i>=0 && j>=0 && i<mpt_size.x && j<mpt_size.y)
				result.push_back(mpt_entities[i*mpt_size.y + j]);
		}

		return result;
	}
	else
		return std::vector<int>();
}


void EntityMap::deleteEntity(int id)
{
	std::map<int, sf::Vector2i>::iterator itr = mpt_entityIDtoPos.find(id);
	if(itr != mpt_entityIDtoPos.end())
	{
		sf::Vector2i position = itr->second;
		mpt_entities[position.x * mpt_size.y + position.y] = 0;
		mpt_entityIDtoPos.erase(itr);
	}
}

sf::Vector2i EntityMap::getSize() const
{
	return mpt_size;
}